#ifndef _LSTM_MODEL_AUTO_SECOND_HH
#define _LSTM_MODEL_AUTO_SECOND_HH

#define innode  3
#define hidenode_1 3
#define hidenode_2 3
#define hidenode_3 3
#define hidenode_4 3
#define outnode  3
#define time_step 20

#include "iostream"
#include "math.h"
#include "stdlib.h"
#include "vector"
#include "assert.h"
#include "string.h"
#include <fstream>
#include <sstream>

using namespace  std;

//Activation Function
float sigmoid(float x)
{
    return float(1.0 / (1.0 + exp(-x)));
}

void file_to_string(vector<string> &record, const string& line, char delimiter)
{
    int linepos=0;
    char c;
    int linemax=line.length();
    string curstring;
    record.clear();
    while(linepos<linemax)
    {
        c = line[linepos];
        if(isdigit(c)||c=='.'||c=='-'){
            curstring+=c;
        }
        else if(c==delimiter&&curstring.size()){
            record.push_back(curstring);
            curstring="";
        }
        ++linepos;
    }
    if(curstring.size())
        record.push_back(curstring);
}

template <class Type>  
Type stringToNum(const string& str)  
{  
    istringstream iss(str);  
    Type num;  
    iss >> num;  
    return num;      
}

void getW1Data(string filePath, float array[innode][hidenode_1])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getW2Data(string filePath, float array[hidenode_1][hidenode_2])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getW3Data(string filePath, float array[hidenode_2][hidenode_3])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getW4Data(string filePath, float array[hidenode_3][hidenode_4])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getU1Data(string filePath, float array[hidenode_1][hidenode_1])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getU2Data(string filePath, float array[hidenode_2][hidenode_2])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getU3Data(string filePath, float array[hidenode_3][hidenode_3])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getU4Data(string filePath, float array[hidenode_4][hidenode_4])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getB1Data(string filePath, float array[hidenode_1])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1]= stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getB2Data(string filePath, float array[hidenode_2])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1]= stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getB3Data(string filePath, float array[hidenode_3])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1]= stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getB4Data(string filePath, float array[hidenode_4])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1]= stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getWOutData(string filePath, float array[hidenode_4][outnode])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1][i-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

void getBOutData(string filePath, float array[outnode])
{ 
    vector<string> row;
    string line;
    ifstream in(filePath);
    if (in.fail())  { cout << "File not found" <<endl; assert(0); }
    int num_lines = 0;
    while(getline(in, line) && in.good())
    {
        file_to_string(row, line, ',');
        if (num_lines > 0) {
            for(int i=1, leng=row.size(); i<leng; i++){
             //cout<<row[i]<<endl;
             array[num_lines-1] = stringToNum<float>(row[i]);
            }
        }
        num_lines++;
    }
    in.close();
}

template<class T>
struct Matrix
{
    Matrix(){}
    Matrix(int xdim,int ydim)
    {
        resize(xdim,ydim);
    }
    void resize(int xdim,int ydim){_xdim=xdim;_ydim=ydim;data.resize(_xdim*_ydim);}
    T& at(int x,int y){return data[_xdim*y+x];}
    void clear(){data.clear();_xdim=_ydim=0;}
    T* line(int y){return &(at(0,y));}
private:
    int _xdim,_ydim;
    vector<T> data;
};


class RNN
{
public:
    RNN();
    virtual ~RNN();
    void rnn_predict(vector<float> &, float x_testdata_window[time_step][innode]);

private:

    // LSTM layer1
    float W_I_1[innode][hidenode_1] = {{-0.28974849, -2.42773628, 1.54282212}, {2.53692675, 0.03950362, -3.41583657}, {-5.81096935, 2.66963053, 8.59975147}};       
    float U_I_1[hidenode_1][hidenode_1] = {{0.30297258, 4.06174612, 3.03012037}, {0.08723351, -4.39431524, -0.70799822}, {-2.20919609, 6.45223856, -1.80022728}};
    float B_I_1[hidenode_1] = {0.59075540, 2.18092752, -2.70036817};
    float W_F_1[innode][hidenode_1] = {{-2.23193145, -0.24992791, 1.15704453}, {-9.48383331, -1.18379366, -0.63322443}, {4.47531891, 0.60419309, 8.60233116}};
    float U_F_1[hidenode_1][hidenode_1] = {{5.31573820, -6.19804716, -8.02526569}, {0.95191509, 1.47060132, -0.63449603}, {3.00124645, -5.90331841, 3.40186548}};
    float B_F_1[hidenode_1] = {-0.03117196, 0.07189102, 2.43932724};
    float W_G_1[innode][hidenode_1] = {{4.19337797, -1.82927406, 0.47327802}, {-5.21560860, -0.56978726, -2.89202070}, {2.54096150, -0.20101604, -2.80587387}};
    float U_G_1[hidenode_1][hidenode_1] = {{3.14959145, 0.72191334, 0.82117182}, {-0.07421258, -0.38485065, -1.84254527}, {0.84828287, 0.03936843, 5.60276508}};
    float B_G_1[hidenode_1] = {0.95768017, 0.89501691, 0.22317220};
    float W_O_1[innode][hidenode_1] = {{0.17559873, 2.22851849, 0.24574229}, {-5.66408205, 1.93352318, -3.83404303}, {1.58805048, -0.07590353, 3.14620233}};
    float U_O_1[hidenode_1][hidenode_1] = {{0.97192210, 2.08921671, 0.25694507}, {-1.35097969, 3.82680154, -0.91207069}, {-0.55709797, 1.13516545, -0.43813780}};
    float B_O_1[hidenode_1] = {0.17731036, -1.20337713, -0.07069180};

    // LSTM layer2
    float W_I_2[hidenode_1][hidenode_2] = {{4.30343962, 2.87018251, 2.91414785}, {-2.43378448, -4.18652153, 2.81746507}, {-3.00184441, -0.46846899, -0.78606558}};
    float U_I_2[hidenode_2][hidenode_2] = {{0.70804417, -0.65436214, 0.79611111}, {-0.88933748, -1.54557109, -1.04259527}, {1.81980562, -1.02158737, 4.32313681}};
    float B_I_2[hidenode_2] = {-2.16710782, -1.27291882, -0.28304532};
    float W_F_2[hidenode_1][hidenode_2] = {{-3.46210194, 3.35639572, 3.46762061}, {1.16214180, 1.74553919, 0.81895787}, {1.62482047, -4.46745300, 1.30162895}};
    float U_F_2[hidenode_2][hidenode_2] = {{1.12755072, -0.14069469, 0.20191523}, {-0.47515064, 0.22794370, -0.64077312}, {2.16410089, 2.25719213, 3.26829410}};
    float B_F_2[hidenode_2] = {1.14186239, -1.43820345, -0.19203325};
    float W_G_2[hidenode_1][hidenode_2] = {{-1.91757798, -2.31356215, -1.20318675}, {4.39009333, 1.06266892, -2.49740100}, {2.09862852, -2.22532201, -0.21749148}};
    float U_G_2[hidenode_2][hidenode_2] = {{2.83631015, -0.14008507, 0.37514201}, {2.28404760, -1.59661281, -0.81573814}, {-3.98761964, 0.25095177, -1.47315121}};
    float B_G_2[hidenode_2] = {1.23242307, 0.35095531, -0.08621097};
    float W_O_2[hidenode_1][hidenode_2] = {{11.50513077, 4.72561312, 2.55660319}, {1.08738732, 13.84823895, -0.16890790}, {-1.84867704, -2.99843311, 0.39093339}};
    float U_O_2[hidenode_2][hidenode_2] = {{-1.39346373, 0.03656165, 0.41542399}, {-5.04041815, 3.55795145, 3.24992967}, {-1.48931313, -10.65347958, -2.18128991}};
    float B_O_2[hidenode_2] = {-0.88894826, 4.36806583, -0.12831080};



    // LSTM layer3
    float W_I_3[hidenode_2][hidenode_3] = {{0.19964601, -1.84003532, -2.55866385}, {9.72070312, -0.27416530, 3.19371271}, {-1.32778299, 3.52249956, -0.82396358}};
    float U_I_3[hidenode_3][hidenode_3] = {{-0.06447925, -7.96248627, -1.94311416}, {0.36825264, 2.10337305, 1.94035435}, {-1.78686094, -5.65139341, 4.18612909}};
    float B_I_3[hidenode_3] = {3.93922925, 0.17545711, 2.97617435};
    float W_F_3[hidenode_2][hidenode_3] = {{1.63851607, -15.50821877, 1.42863393}, {-3.62526107, -2.86268926, -13.46417236}, {-0.00541432, -2.50024915, 0.68001771}};
    float U_F_3[hidenode_3][hidenode_3] = {{-2.34840679, -2.67071009, 4.10352182}, {-4.61663055, -3.69326234, -2.30987811}, {3.29905462, -0.93560761, 0.93578529}};
    float B_F_3[hidenode_3] = {-0.65996188, -1.30098534, 1.41602647};
    float W_G_3[hidenode_2][hidenode_3] = {{-2.10826850, 1.30127001, -1.87205100}, {-0.43500161, 5.85407305, 3.23218942}, {9.02338123, 1.13030255, 0.71069354}};
    float U_G_3[hidenode_3][hidenode_3] = {{-1.23106349, 0.14881822, -0.05860889}, {-0.10095301, -1.60391402, 0.35228753}, {-0.94272572, -2.83438730, -0.09782084}};
    float B_G_3[hidenode_3] = {0.34162265, -0.19094992, -0.28366691};
    float W_O_3[hidenode_2][hidenode_3] = {{-0.96793318, 1.52742243, 2.38590574}, {0.43347079, 2.11908555, 0.00058897}, {-3.69690561, 0.84047216, 3.09055090}};
    float U_O_3[hidenode_3][hidenode_3] = {{1.25213528, 0.65602905, 1.81879783}, {0.47401416, -1.84599054, 0.54824179}, {-0.43178847, -0.55625325, -2.79549217}};
    float B_O_3[hidenode_3] {-1.24193335, -0.33185357, 0.96559298};

        // LSTM layer4
    float W_I_4[hidenode_3][hidenode_4] = {{-2.82495117, 2.50935769, 2.98751068}, {-1.50192285, -2.94368863, -5.17983103}, {0.29030728, 1.63341963, 0.76305586}};
    float U_I_4[hidenode_4][hidenode_4] = {{0.55097991, -2.21552181, 3.66354728}, {-0.16161650, -0.37181699, -0.83729935}, {-0.54808766, 1.91108906, 3.80936074}};
    float B_I_4[hidenode_4] = {-0.79293162, 0.09094406, 2.23251796};
    float W_F_4[hidenode_3][hidenode_4] = {{-4.60817671, -1.23277819, 5.39495897}, {0.67146730, -0.58715719, -6.83567858}, {2.25346112, -0.62396967, -0.46495479}};
    float U_F_4[hidenode_4][hidenode_4] = {{-0.20564823, -3.23884225, -0.15820909}, {-0.43015608, -0.63494670, 2.65423036}, {0.28484979, -0.55774420, 0.09476459}};
    float B_F_4[hidenode_4] = {-2.03277159, -3.65922117, 1.99593294};
    float W_G_4[hidenode_3][hidenode_4] = {{10.01137829, -0.85995823, 0.33348939}, {-4.53007126, 3.74764514, -2.08285928}, {0.03585120, 2.99984193, -2.33127832}};
    float U_G_4[hidenode_4][hidenode_4] = {{-0.03594543, 1.70975578, 0.93536514}, {0.20688830, -1.01490450, -0.85208309}, {1.42249179, -0.26304030, -1.64151418}};
    float B_G_4[hidenode_4] = {-2.04379082, 1.59572387, 1.45993507};
    float W_O_4[hidenode_3][hidenode_4] = {{-6.27755165, -11.27503872, 1.55404210}, {-7.52298117, -3.10449123, -1.61242354}, {0.05974979, -7.17959738, -0.86489356}};
    float U_O_4[hidenode_4][hidenode_4] = {{-3.15382504, -2.89701962, -8.07496929}, {-7.86090851, 4.83028841, 0.68499202}, {8.04381466, -2.88571167, 0.88174069}};
    float B_O_4[hidenode_4] = {1.74244189, 3.18481803, -0.76198405};


    // DENSE layer
    float W_out[hidenode_1][outnode] = {{3.88765621, 1.75852561, 0.67302746}, {-1.84627855, 3.14802670, 1.64712703}, {-0.25242543, 0.60576111, -4.95714426}};
    float B_out[outnode] = {0.87127489, -1.58056962, -1.08472407};
 
    vector<float> y;
};

#endif // _LSTM_MODEL_AUTO_SECOND_HH
